using System;
using System.Collections.Generic;
using System.Text;
using Barrage.Global;
using Barrage.Global.Vertex;
using Barrage.Shaders;
using SlimDX.Direct3D9;
using SlimDX;


namespace Barrage.Meshes
{
    /// <summary>
    /// ParticleMesh renders a set of particles using the point sprite technique
    /// </summary>
    public class ParticleMesh : Barrage.Mesh
    {
        /// <summary>
        /// Max number of particles that an instance of ParticleMesh can render
        /// </summary>
        public const int MAX_PARTICLES = 10240;
        
        SlimDX.Direct3D9.VertexBuffer particleBuffer = null;

        int particleNumber = MAX_PARTICLES;

        /// <summary>
        /// Number of particles that will be rendered
        /// </summary>
        public int ParticleNumber { get { return particleNumber; } set { particleNumber = value; } }

        VertexPositionColorSize[] particleData= new VertexPositionColorSize[MAX_PARTICLES];

        /// <summary>
        /// Array holding each particle's data
        /// </summary>
        public VertexPositionColorSize[] ParticleData { get { return particleData; } }

        VertexDeclaration declaration;

        BoundingSphere sphere;

        void initializeBuffer()
        {
            declaration = new VertexDeclaration(Engine.Device, VertexPositionColorSize.Elements);
            particleBuffer = new VertexBuffer(Engine.Device, VertexPositionColorSize.SizeBytes * MAX_PARTICLES, Usage.WriteOnly | Usage.Dynamic , VertexFormat.None, Pool.Default);
            DataStream stream = particleBuffer.Lock(0, particleNumber * VertexPositionColorSize.SizeBytes, LockFlags.None);
            
            Random r = new Random();

            for (int i=0;i<MAX_PARTICLES;i++)
            {
                particleData[i].Position = new Vector3((float)r.NextDouble() * 10, (float)r.NextDouble() * 10, (float)r.NextDouble() * 10);
                particleData[i].Color = new Color4(1, 1, 1, 1);
                particleData[i].Size = 150;
            }
            stream.WriteRange(particleData);
            particleBuffer.Unlock();
            //UpdateSphere();
        }

        void UpdateSphere()
        {
            Vector3[] points = new Vector3[particleNumber];
            for (int i = 0; i < particleNumber; i++)
                points[i] = particleData[i].Position;
            sphere = BoundingSphere.FromPoints(points);
        }

        /// <summary>
        /// Sends particle data to the graphics card
        /// </summary>
        public void UpdateData()
        {
            //UpdateSphere();
            DataStream stream = particleBuffer.Lock(0, particleNumber * VertexPositionColorSize.SizeBytes, LockFlags.None);
            stream.WriteRange(particleData,0,particleNumber);
            particleBuffer.Unlock();
        }
        /// <summary>
        /// Constructor. Sets the specified texture and shader
        /// </summary>
        /// <param name="source">Texture used to render the particles</param>
        /// <param name="shader">Shader used to render the particles</param>
        public ParticleMesh(BaseTexture source,Shader shader)
        {
            initializeBuffer();
            this.shader = shader;
            Material m = new Material();
            m.Texture = source;
            materials.Add(m);
        }

        /// <summary>
        /// Constructor. Sets the specified texture
        /// </summary>
        /// <param name="source">Texture used to render the particles</param>
        public ParticleMesh(BaseTexture source)
        {
            initializeBuffer();
            shader = ParticleShader.Instance;
            Material m = new Material();
            m.Texture = source;
            materials.Add(m);
        }

        /// <summary>
        /// \internal Renders all of the currently alive particles
        /// </summary>
        /// <param name="item">Item that's rendering this mesh</param>
        public override void Render(Item item)
        {
            if (particleNumber > 0)
            {
                RenderStats.RenderCalls++;
                /*if (Scene.CurrentInstance.Camera.CheckCull(sphere,item.Motion))
                {
                    RenderStats.CulledObjects ++;
                    return;
                }*/
                shader.BeginRender(item, materials[0]);

                Engine.Device.VertexDeclaration = declaration;
                
                Engine.Device.SetStreamSource(0, particleBuffer, 0, VertexPositionColorSize.SizeBytes);

                int passNumber = shader.PassNumber(materials[0]);
                for (int j = 0; j < passNumber; j++)
                {
                    shader.BeginPass(j);
                    Engine.Device.DrawPrimitives(PrimitiveType.PointList, 0, particleNumber);
                    shader.EndPass();
                } 

                shader.EndRender();
            }

            NLog.LogManager.GetLogger("Barrage::Scene::Graphics3D::PostprocessMesh").Debug("Rendering Cube");
        }

        /// <summary>
        /// \internal Clear resources
        /// </summary>
        public override void Dispose()
        {
            this.declaration.Dispose();
            particleBuffer.Dispose();
            particleBuffer = null;
            base.Dispose();
        }
    }
}
